home *** CD-ROM | disk | FTP | other *** search
/ Freelog 125 / Freelog_MarsAvril2015_No125.iso / ViePratique / gnucash / gnucash-2.6.5-setup.exe / {app} / bin / intltool-update < prev    next >
Text File  |  2014-11-01  |  32KB  |  1,199 lines

  1. #!/bin/perl -w
  2. # -*- Mode: perl; indent-tabs-mode: nil; c-basic-offset: 4  -*-
  3.  
  4. #
  5. #  The Intltool Message Updater
  6. #
  7. #  Copyright (C) 2000-2003 Free Software Foundation.
  8. #
  9. #  Intltool is free software; you can redistribute it and/or
  10. #  modify it under the terms of the GNU General Public License 
  11. #  version 2 published by the Free Software Foundation.
  12. #
  13. #  Intltool is distributed in the hope that it will be useful,
  14. #  but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16. #  General Public License for more details.
  17. #
  18. #  You should have received a copy of the GNU General Public License
  19. #  along with this program; if not, write to the Free Software
  20. #  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21. #
  22. #  As a special exception to the GNU General Public License, if you
  23. #  distribute this file as part of a program that contains a
  24. #  configuration script generated by Autoconf, you may include it under
  25. #  the same distribution terms that you use for the rest of that program.
  26. #
  27. #  Authors: Kenneth Christiansen <kenneth@gnu.org>
  28. #           Maciej Stachowiak
  29. #           Darin Adler <darin@bentspoon.com>
  30.  
  31. ## Release information
  32. my $PROGRAM = "intltool-update";
  33. my $VERSION = "0.40.4";
  34. my $PACKAGE = "intltool";
  35.  
  36. ## Loaded modules
  37. use strict;
  38. use Getopt::Long;
  39. use Cwd;
  40. use File::Copy;
  41. use File::Find;
  42.  
  43. ## Scalars used by the option stuff
  44. my $HELP_ARG        = 0;
  45. my $VERSION_ARG    = 0;
  46. my $DIST_ARG       = 0;
  47. my $POT_ARG       = 0;
  48. my $HEADERS_ARG    = 0;
  49. my $MAINTAIN_ARG   = 0;
  50. my $REPORT_ARG     = 0;
  51. my $VERBOSE       = 0;
  52. my $GETTEXT_PACKAGE = "";
  53. my $OUTPUT_FILE    = "";
  54.  
  55. my @languages;
  56. my %varhash = ();
  57. my %po_files_by_lang = ();
  58.  
  59. # Regular expressions to categorize file types.
  60. # FIXME: Please check if the following is correct
  61.  
  62. my $xml_support =
  63. "xml(?:\\.in)*|".    # http://www.w3.org/XML/ (Note: .in is not required)
  64. "ui|".            # Bonobo specific - User Interface desc. files
  65. "lang|".        # ?
  66. "glade2?(?:\\.in)*|".    # Glade specific - User Interface desc. files (Note: .in is not required)
  67. "scm(?:\\.in)*|".    # ? (Note: .in is not required)
  68. "oaf(?:\\.in)+|".    # DEPRECATED: Replaces by Bonobo .server files 
  69. "etspec|".        # ?
  70. "server(?:\\.in)+|".    # Bonobo specific
  71. "sheet(?:\\.in)+|".    # ?
  72. "schemas(?:\\.in)+|".    # GConf specific
  73. "pong(?:\\.in)+|".    # DEPRECATED: PONG is not used [by GNOME] any longer.
  74. "kbd(?:\\.in)+|".    # GOK specific. 
  75. "policy(?:\\.in)+";    # PolicyKit files
  76.  
  77. my $ini_support =
  78. "icon(?:\\.in)+|".    # http://www.freedesktop.org/Standards/icon-theme-spec
  79. "desktop(?:\\.in)+|".    # http://www.freedesktop.org/Standards/menu-spec
  80. "caves(?:\\.in)+|".    # GNOME Games specific
  81. "directory(?:\\.in)+|".    # http://www.freedesktop.org/Standards/menu-spec
  82. "soundlist(?:\\.in)+|".    # GNOME specific
  83. "keys(?:\\.in)+|".    # GNOME Mime database specific
  84. "theme(?:\\.in)+|".    # http://www.freedesktop.org/Standards/icon-theme-spec
  85. "service(?:\\.in)+";    # DBus specific
  86.  
  87. my $buildin_gettext_support = 
  88. "c|y|cs|cc|cpp|c\\+\\+|h|hh|gob|py";
  89.  
  90. ## Always flush buffer when printing
  91. $| = 1;
  92.  
  93. ## Sometimes the source tree will be rooted somewhere else.
  94. my $SRCDIR = $ENV{"srcdir"} || ".";
  95. my $POTFILES_in;
  96.  
  97. $POTFILES_in = "<$SRCDIR/POTFILES.in";
  98.  
  99. my $devnull = ($^O eq 'MSWin32' ? 'NUL:' : '/dev/null');
  100.  
  101. ## Handle options
  102. GetOptions 
  103. (
  104.  "help"            => \$HELP_ARG,
  105.  "version"            => \$VERSION_ARG,
  106.  "dist|d"           => \$DIST_ARG,
  107.  "pot|p"           => \$POT_ARG,
  108.  "headers|s"           => \$HEADERS_ARG,
  109.  "maintain|m"           => \$MAINTAIN_ARG,
  110.  "report|r"           => \$REPORT_ARG,
  111.  "verbose|x"           => \$VERBOSE,
  112.  "gettext-package|g=s" => \$GETTEXT_PACKAGE,
  113.  "output-file|o=s"     => \$OUTPUT_FILE,
  114.  ) or &Console_WriteError_InvalidOption;
  115.  
  116. &Console_Write_IntltoolHelp if $HELP_ARG;
  117. &Console_Write_IntltoolVersion if $VERSION_ARG;
  118.  
  119. my $arg_count = ($DIST_ARG > 0)
  120.     + ($POT_ARG > 0)
  121.     + ($HEADERS_ARG > 0)
  122.     + ($MAINTAIN_ARG > 0)
  123.     + ($REPORT_ARG > 0);
  124.  
  125. &Console_Write_IntltoolHelp if $arg_count > 1;
  126.  
  127. my $MODULE = $GETTEXT_PACKAGE || FindPackageName() || "unknown";
  128.  
  129. if ($POT_ARG)
  130. {
  131.     &GenerateHeaders;
  132.     &GeneratePOTemplate;
  133. }
  134. elsif ($HEADERS_ARG)
  135. {
  136.     &GenerateHeaders;
  137. }
  138. elsif ($MAINTAIN_ARG)
  139. {
  140.     &FindLeftoutFiles;
  141. }
  142. elsif ($REPORT_ARG)
  143. {
  144.     &GenerateHeaders;
  145.     &GeneratePOTemplate;
  146.     &Console_Write_CoverageReport;
  147. }
  148. elsif ((defined $ARGV[0]) && $ARGV[0] =~ /^[a-z]/)
  149. {
  150.     my $lang = $ARGV[0];
  151.  
  152.     ## Report error if the language file supplied
  153.     ## to the command line is non-existent
  154.     &Console_WriteError_NotExisting("$SRCDIR/$lang.po")
  155.         if ! -s "$SRCDIR/$lang.po";
  156.  
  157.     if (!$DIST_ARG)
  158.     {
  159.     print "Working, please wait..." if $VERBOSE;
  160.     &GenerateHeaders;
  161.     &GeneratePOTemplate;
  162.     }
  163.     &POFile_Update ($lang, $OUTPUT_FILE);
  164.     &Console_Write_TranslationStatus ($lang, $OUTPUT_FILE);
  165. else 
  166. {
  167.     &Console_Write_IntltoolHelp;
  168. }
  169.  
  170. exit;
  171.  
  172. #########
  173.  
  174. sub Console_Write_IntltoolVersion
  175. {
  176.     print <<_EOF_;
  177. ${PROGRAM} (${PACKAGE}) $VERSION
  178. Written by Kenneth Christiansen, Maciej Stachowiak, and Darin Adler.
  179.  
  180. Copyright (C) 2000-2003 Free Software Foundation, Inc.
  181. This is free software; see the source for copying conditions.  There is NO
  182. warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  183. _EOF_
  184.     exit;
  185. }
  186.  
  187. sub Console_Write_IntltoolHelp
  188. {
  189.     print <<_EOF_;
  190. Usage: ${PROGRAM} [OPTION]... LANGCODE
  191. Updates PO template files and merge them with the translations.
  192.  
  193. Mode of operation (only one is allowed):
  194.   -p, --pot                   generate the PO template only
  195.   -s, --headers               generate the header files in POTFILES.in
  196.   -m, --maintain              search for left out files from POTFILES.in
  197.   -r, --report                display a status report for the module
  198.   -d, --dist                  merge LANGCODE.po with existing PO template
  199.  
  200. Extra options:
  201.   -g, --gettext-package=NAME  override PO template name, useful with --pot
  202.   -o, --output-file=FILE      write merged translation to FILE
  203.   -x, --verbose               display lots of feedback
  204.       --help                  display this help and exit
  205.       --version               output version information and exit
  206.  
  207. Examples of use:
  208. ${PROGRAM} --pot    just create a new PO template
  209. ${PROGRAM} xy       create new PO template and merge xy.po with it
  210.  
  211. Report bugs to http://bugzilla.gnome.org/ (product name "$PACKAGE")
  212. or send email to <xml-i18n-tools\@gnome.org>.
  213. _EOF_
  214.     exit;
  215. }
  216.  
  217. sub echo_n
  218. {
  219.     my $str = shift;
  220.     my $ret = `echo "$str"`;
  221.  
  222.     $ret =~ s/\n$//; # do we need the "s" flag?
  223.  
  224.     return $ret;
  225. }
  226.  
  227. sub POFile_DetermineType ($) 
  228. {
  229.    my $type = $_;
  230.    my $gettext_type;
  231.  
  232.    my $xml_regex     = "(?:" . $xml_support . ")";
  233.    my $ini_regex     = "(?:" . $ini_support . ")";
  234.    my $buildin_regex = "(?:" . $buildin_gettext_support . ")";
  235.  
  236.    if ($type =~ /\[type: gettext\/([^\]].*)]/) 
  237.    {
  238.     $gettext_type=$1;
  239.    }
  240.    elsif ($type =~ /schemas(\.in)+$/) 
  241.    {
  242.     $gettext_type="schemas";
  243.    }
  244.    elsif ($type =~ /glade2?(\.in)*$/) 
  245.    {
  246.        $gettext_type="glade";
  247.    }
  248.    elsif ($type =~ /scm(\.in)*$/) 
  249.    {
  250.        $gettext_type="scheme";
  251.    }
  252.    elsif ($type =~ /keys(\.in)+$/) 
  253.    {
  254.        $gettext_type="keys";
  255.    }
  256.  
  257.    # bucket types
  258.  
  259.    elsif ($type =~ /$xml_regex$/) 
  260.    {
  261.        $gettext_type="xml";
  262.    }
  263.    elsif ($type =~ /$ini_regex$/) 
  264.    { 
  265.        $gettext_type="ini";
  266.    }
  267.    elsif ($type =~ /$buildin_regex$/) 
  268.    {
  269.        $gettext_type="buildin";
  270.    }
  271.    else
  272.    { 
  273.        $gettext_type="unknown"; 
  274.    }
  275.  
  276.    return "gettext\/$gettext_type";
  277. }
  278.  
  279. sub TextFile_DetermineEncoding ($) 
  280. {
  281.     my $gettext_code="UTF-8"; # All files are UTF-8 by default
  282.     my $filetype=`file $_ | cut -d ' ' -f 2`;
  283.  
  284.     if ($? eq "0")
  285.     {
  286.     if ($filetype =~ /^(ISO|UTF)/)
  287.     {
  288.         chomp ($gettext_code = $filetype);
  289.     }
  290.     elsif ($filetype =~ /^XML/)
  291.     {
  292.         $gettext_code="UTF-8"; # We asume that .glade and other .xml files are UTF-8
  293.     }
  294.     }
  295.  
  296.     return $gettext_code;
  297. }
  298.  
  299. sub isNotValidMissing
  300. {
  301.     my ($file) = @_;
  302.  
  303.     return if $file =~ /^\{arch\}\/.*$/;
  304.     return if $file =~ /^$varhash{"PACKAGE"}-$varhash{"VERSION"}\/.*$/;
  305. }
  306.  
  307. sub FindLeftoutFiles
  308. {
  309.     my (@buf_i18n_plain,
  310.     @buf_i18n_xml,
  311.     @buf_i18n_xml_unmarked,
  312.     @buf_i18n_ini,
  313.     @buf_potfiles,
  314.     @buf_potfiles_ignore,
  315.     @buf_allfiles,
  316.     @buf_allfiles_sorted,
  317.     @buf_potfiles_sorted,
  318.         @buf_potfiles_ignore_sorted
  319.     );
  320.  
  321.     ## Search and find all translatable files
  322.     find sub { 
  323.     push @buf_i18n_plain,        "$File::Find::name" if /\.($buildin_gettext_support)$/;
  324.     push @buf_i18n_xml,          "$File::Find::name" if /\.($xml_support)$/;
  325.     push @buf_i18n_ini,          "$File::Find::name" if /\.($ini_support)$/;
  326.     push @buf_i18n_xml_unmarked, "$File::Find::name" if /\.(schemas(\.in)+)$/;
  327.     }, "..";
  328.     find sub { 
  329.     push @buf_i18n_plain,        "$File::Find::name" if /\.($buildin_gettext_support)$/;
  330.     push @buf_i18n_xml,          "$File::Find::name" if /\.($xml_support)$/;
  331.     push @buf_i18n_ini,          "$File::Find::name" if /\.($ini_support)$/;
  332.     push @buf_i18n_xml_unmarked, "$File::Find::name" if /\.(schemas(\.in)+)$/;
  333.     }, "$SRCDIR/.." if "$SRCDIR" ne ".";
  334.  
  335.     open POTFILES, $POTFILES_in or die "$PROGRAM:  there's no POTFILES.in!\n";
  336.     @buf_potfiles = grep !/^(#|\s*$)/, <POTFILES>;
  337.     close POTFILES;
  338.  
  339.     foreach (@buf_potfiles) {
  340.     s/^\[.*]\s*//;
  341.     }
  342.  
  343.     print "Searching for missing translatable files...\n" if $VERBOSE;
  344.  
  345.     ## Check if we should ignore some found files, when
  346.     ## comparing with POTFILES.in
  347.     foreach my $ignore ("POTFILES.skip", "POTFILES.ignore")
  348.     {
  349.     (-s "$SRCDIR/$ignore") or next;
  350.  
  351.     if ("$ignore" eq "POTFILES.ignore")
  352.     {
  353.         print "The usage of POTFILES.ignore is deprecated. Please consider moving the\n".
  354.           "content of this file to POTFILES.skip.\n";
  355.     }
  356.  
  357.     print "Found $ignore: Ignoring files...\n" if $VERBOSE;
  358.     open FILE, "<$SRCDIR/$ignore" or die "ERROR: Failed to open $SRCDIR/$ignore!\n";
  359.         
  360.     while (<FILE>)
  361.     {
  362.             next if (/^$/);
  363.             next if (/^(#|\s*$)/);
  364.  
  365.             my $skipdir = "../$_";
  366.             $skipdir = "$SRCDIR/../$_" if "$SRCDIR" ne ".";
  367.             $skipdir =~ s/\n//g;
  368.  
  369.             my @dirignored;
  370.  
  371.             if (-d "$skipdir")
  372.             {
  373.                 find sub {
  374.                     push @dirignored, "$File::Find::name" if /\.($buildin_gettext_support)$/;
  375.                     push @dirignored, "$File::Find::name" if /\.($xml_support)$/;
  376.                     push @dirignored, "$File::Find::name" if /\.($ini_support)$/;
  377.                     push @dirignored, "$File::Find::name" if /\.(schemas(\.in)+)$/;
  378.                 }, "$skipdir";
  379.                 foreach my $ignored (@dirignored)
  380.                 {
  381.                     $ignored =~ s/^$SRCDIR\///g;
  382.                     $ignored =~ s/^..\///g;
  383.                     $ignored =~ s/$/\n/g;
  384.                     push @buf_potfiles_ignore, $ignored;
  385.                 }
  386.                 next;
  387.             }
  388.             push @buf_potfiles_ignore, $_;
  389.     }
  390.     close FILE;
  391.  
  392.     @buf_potfiles_ignore_sorted = sort (@buf_potfiles_ignore);
  393.     }
  394.  
  395.     foreach my $file (@buf_i18n_plain)
  396.     {
  397.     my $in_comment = 0;
  398.     my $in_macro = 0;
  399.  
  400.     open FILE, "<$file";
  401.     while (<FILE>)
  402.     {
  403.         # Handle continued multi-line comment.
  404.         if ($in_comment)
  405.         {
  406.         next unless s-.*\*/--;
  407.         $in_comment = 0;
  408.         }
  409.  
  410.         # Handle continued macro.
  411.         if ($in_macro)
  412.         {
  413.         $in_macro = 0 unless /\\$/;
  414.         next;
  415.         }
  416.  
  417.         # Handle start of macro (or any preprocessor directive).
  418.         if (/^\s*\#/)
  419.         {
  420.         $in_macro = 1 if /^([^\\]|\\.)*\\$/;
  421.         next;
  422.         }
  423.  
  424.         # Handle comments and quoted text.
  425.         while (m-(/\*|//|\'|\")-) # \' and \" keep emacs perl mode happy
  426.         {
  427.         my $match = $1;
  428.         if ($match eq "/*")
  429.         {
  430.             if (!s-/\*.*?\*/--)
  431.             {
  432.             s-/\*.*--;
  433.             $in_comment = 1;
  434.             }
  435.         }
  436.         elsif ($match eq "//")
  437.         {
  438.             s-//.*--;
  439.         }
  440.         else # ' or "
  441.         {
  442.             if (!s-$match([^\\]|\\.)*?$match-QUOTEDTEXT-)
  443.             {
  444.             warn "mismatched quotes at line $. in $file\n";
  445.             s-$match.*--;
  446.             }
  447.         }
  448.         }        
  449.  
  450.         if (/\w\.GetString *\(QUOTEDTEXT/)
  451.         {
  452.                 if (defined isNotValidMissing (unpack("x3 A*", $file))) {
  453.                     ## Remove the first 3 chars and add newline
  454.                     push @buf_allfiles, unpack("x3 A*", $file) . "\n";
  455.                 }
  456.         last;
  457.         }
  458.  
  459.             ## C_ N_ NC_ Q_ and _ are the macros defined in gi8n.h
  460.         if (/(NC|[CNQ]?)_ *\(QUOTEDTEXT/)
  461.         {
  462.                 if (defined isNotValidMissing (unpack("x3 A*", $file))) {
  463.                     ## Remove the first 3 chars and add newline
  464.                     push @buf_allfiles, unpack("x3 A*", $file) . "\n";
  465.                 }
  466.         last;
  467.         }
  468.     }
  469.     close FILE;
  470.     }
  471.  
  472.     foreach my $file (@buf_i18n_xml) 
  473.     {
  474.     open FILE, "<$file";
  475.     
  476.     while (<FILE>) 
  477.     {
  478.         # FIXME: share the pattern matching code with intltool-extract
  479.         if (/\s_[-A-Za-z0-9._:]+\s*=\s*\"([^"]+)\"/ || /<_[^>]+>/ || /translatable=\"yes\"/)
  480.         {
  481.                 if (defined isNotValidMissing (unpack("x3 A*", $file))) {
  482.                     push @buf_allfiles, unpack("x3 A*", $file) . "\n";
  483.                 }
  484.         last;
  485.         }
  486.     }
  487.     close FILE;
  488.     }
  489.  
  490.     foreach my $file (@buf_i18n_ini)
  491.     {
  492.     open FILE, "<$file";
  493.     while (<FILE>) 
  494.     {
  495.         if (/_(.*)=/)
  496.         {
  497.                 if (defined isNotValidMissing (unpack("x3 A*", $file))) {
  498.                     push @buf_allfiles, unpack("x3 A*", $file) . "\n";
  499.                 }
  500.         last;
  501.         }
  502.     }
  503.     close FILE;
  504.     }
  505.  
  506.     foreach my $file (@buf_i18n_xml_unmarked)
  507.     {
  508.         if (defined isNotValidMissing (unpack("x3 A*", $file))) {
  509.             push @buf_allfiles, unpack("x3 A*", $file) . "\n";
  510.         }
  511.     }
  512.  
  513.  
  514.     @buf_allfiles_sorted = sort (@buf_allfiles);
  515.     @buf_potfiles_sorted = sort (@buf_potfiles);
  516.  
  517.     my %in2;
  518.     foreach (@buf_potfiles_sorted) 
  519.     {
  520.         s#^$SRCDIR/../##;
  521.         s#^$SRCDIR/##;
  522.     $in2{$_} = 1;
  523.     }
  524.  
  525.     foreach (@buf_potfiles_ignore_sorted) 
  526.     {
  527.         s#^$SRCDIR/../##;
  528.         s#^$SRCDIR/##;
  529.     $in2{$_} = 1;
  530.     }
  531.  
  532.     my @result;
  533.  
  534.     foreach (@buf_allfiles_sorted)
  535.     {
  536.         my $dummy = $_;
  537.         my $srcdir = $SRCDIR;
  538.  
  539.         $srcdir =~ s#^../##;
  540.         $dummy =~ s#^$srcdir/../##;
  541.         $dummy =~ s#^$srcdir/##;
  542.         $dummy =~ s#_build/##;
  543.     if (!exists($in2{$dummy}))
  544.     {
  545.         push @result, $dummy
  546.     }
  547.     }
  548.  
  549.     my @buf_potfiles_notexist;
  550.  
  551.     foreach (@buf_potfiles_sorted)
  552.     {
  553.     chomp (my $dummy = $_);
  554.     if ("$dummy" ne "" and !(-f "$SRCDIR/../$dummy" or -f "../$dummy"))
  555.     {
  556.         push @buf_potfiles_notexist, $_;
  557.     }
  558.     }
  559.  
  560.     ## Save file with information about the files missing
  561.     ## if any, and give information about this procedure.
  562.     if (@result + @buf_potfiles_notexist > 0)
  563.     {
  564.     if (@result) 
  565.     {
  566.         print "\n" if $VERBOSE;
  567.         unlink "missing";
  568.         open OUT, ">missing";
  569.         print OUT @result;
  570.         close OUT;
  571.         warn "\e[1mThe following files contain translations and are currently not in use. Please\e[0m\n".
  572.              "\e[1mconsider adding these to the POTFILES.in file, located in the po/ directory.\e[0m\n\n";
  573.         print STDERR @result, "\n";
  574.         warn "If some of these files are left out on purpose then please add them to\n".
  575.          "POTFILES.skip instead of POTFILES.in. A file \e[1m'missing'\e[0m containing this list\n".
  576.          "of left out files has been written in the current directory.\n";
  577.     }
  578.     if (@buf_potfiles_notexist)
  579.     {
  580.         unlink "notexist";
  581.         open OUT, ">notexist";
  582.         print OUT @buf_potfiles_notexist;
  583.         close OUT;
  584.         warn "\n" if ($VERBOSE or @result);
  585.         warn "\e[1mThe following files do not exist anymore:\e[0m\n\n";
  586.         warn @buf_potfiles_notexist, "\n";
  587.         warn "Please remove them from POTFILES.in. A file \e[1m'notexist'\e[0m\n".
  588.          "containing this list of absent files has been written in the current directory.\n";
  589.     }
  590.     }
  591.  
  592.     ## If there is nothing to complain about, notify the user
  593.     else {
  594.     print "\nAll files containing translations are present in POTFILES.in.\n" if $VERBOSE;
  595.     }
  596. }
  597.  
  598. sub Console_WriteError_InvalidOption
  599. {
  600.     ## Handle invalid arguments
  601.     print STDERR "Try `${PROGRAM} --help' for more information.\n";
  602.     exit 1;
  603. }
  604.  
  605. sub isProgramInPath
  606. {
  607.     my ($file) = @_;
  608.     # If either a file exists, or when run it returns 0 exit status
  609.     return 1 if ((-x $file) or (system("$file --version >$devnull") == 0));
  610.     return 0;
  611. }
  612.  
  613. sub isGNUGettextTool
  614. {
  615.     my ($file) = @_;
  616.     # Check that we are using GNU gettext tools
  617.     if (isProgramInPath ($file))
  618.     {
  619.         my $version = `$file --version`;
  620.         return 1 if ($version =~ m/.*\(GNU .*\).*/);
  621.     }
  622.     return 0;
  623. }
  624.  
  625. sub GenerateHeaders
  626. {
  627.     my $EXTRACT = $ENV{"INTLTOOL_EXTRACT"} || "intltool-extract";
  628.  
  629.     $EXTRACT = "$^X $EXTRACT" if ($^O eq 'MSWin32' && !($EXTRACT =~ /perl/));
  630.  
  631.     ## Generate the .h header files, so we can allow glade and
  632.     ## xml translation support
  633.     if (! isProgramInPath ("$EXTRACT"))
  634.     {
  635.     print STDERR "\n *** The intltool-extract script wasn't found!"
  636.          ."\n *** Without it, intltool-update can not generate files.\n";
  637.     exit;
  638.     }
  639.     else
  640.     {
  641.     open (FILE, $POTFILES_in) or die "$PROGRAM: POTFILES.in not found.\n";
  642.     
  643.     while (<FILE>) 
  644.     {
  645.        chomp;
  646.        next if /^\[\s*encoding/;
  647.  
  648.        ## Find xml files in POTFILES.in and generate the
  649.        ## files with help from the extract script
  650.  
  651.        my $gettext_type= &POFile_DetermineType ($1);
  652.  
  653.        if (/\.($xml_support|$ini_support)$/ || /^\[/)
  654.        {
  655.            s/^\[[^\[].*]\s*//;
  656.  
  657.            my $filename = "../$_";
  658.  
  659.            if ($VERBOSE)
  660.            {
  661.            system ($EXTRACT, "--update", "--srcdir=$SRCDIR",
  662.                "--type=$gettext_type", $filename);
  663.            } 
  664.            else 
  665.            {
  666.             system ($EXTRACT, "--update", "--type=$gettext_type", 
  667.                "--srcdir=$SRCDIR", "--quiet", $filename);
  668.            }
  669.        }
  670.        }
  671.        close FILE;
  672.    }
  673. }
  674.  
  675. #
  676. # Generate .pot file from POTFILES.in
  677. #
  678. sub GeneratePOTemplate
  679. {
  680.     my $XGETTEXT = $ENV{"XGETTEXT"} || "xgettext";
  681.     my $XGETTEXT_ARGS = $ENV{"XGETTEXT_ARGS"} || '';
  682.     chomp $XGETTEXT;
  683.  
  684.     if (! isGNUGettextTool ("$XGETTEXT"))
  685.     {
  686.     print STDERR " *** GNU xgettext is not found on this system!\n".
  687.              " *** Without it, intltool-update can not extract strings.\n";
  688.     exit;
  689.     }
  690.  
  691.     print "Building $MODULE.pot...\n" if $VERBOSE;
  692.  
  693.     open INFILE, $POTFILES_in;
  694.     unlink "POTFILES.in.temp";
  695.     open OUTFILE, ">POTFILES.in.temp" or die("Cannot open POTFILES.in.temp for writing");
  696.  
  697.     my $gettext_support_nonascii = 0;
  698.  
  699.     # checks for GNU gettext >= 0.12
  700.     my $dummy = `$XGETTEXT --version --from-code=UTF-8 >$devnull 2>$devnull`;
  701.     if ($? == 0)
  702.     {
  703.     $gettext_support_nonascii = 1;
  704.     }
  705.     else
  706.     {
  707.     # require gnu gettext >= 0.12
  708.     die "$PROGRAM: GNU gettext >= 0.12 is required for intltool\n";
  709.     }
  710.  
  711.     my $encoding = "UTF-8";
  712.     my $forced_gettext_code;
  713.     my @temp_headers;
  714.     my $encoding_problem_is_reported = 0;
  715.  
  716.     while (<INFILE>) 
  717.     {
  718.     next if (/^#/ or /^\s*$/);
  719.  
  720.     chomp;
  721.  
  722.     my $gettext_code;
  723.  
  724.     if (/^\[\s*encoding:\s*(.*)\s*\]/)
  725.     {
  726.         $forced_gettext_code=$1;
  727.     }
  728.     elsif (/\.($xml_support|$ini_support)$/ || /^\[/)
  729.     {
  730.         s/^\[.*]\s*//;
  731.             print OUTFILE "../$_.h\n";
  732.         push @temp_headers, "../$_.h";
  733.         $gettext_code = &TextFile_DetermineEncoding ("../$_.h") if ($gettext_support_nonascii and not defined $forced_gettext_code);
  734.     } 
  735.     else 
  736.     {
  737.             print OUTFILE "$SRCDIR/../$_\n";
  738.         $gettext_code = &TextFile_DetermineEncoding ("$SRCDIR/../$_") if ($gettext_support_nonascii and not defined $forced_gettext_code);
  739.     }
  740.  
  741.     next if (! $gettext_support_nonascii);
  742.  
  743.     if (defined $forced_gettext_code)
  744.     {
  745.         $encoding=$forced_gettext_code;
  746.     }
  747.     elsif (defined $gettext_code and "$encoding" ne "$gettext_code")
  748.     {
  749.         if ($encoding eq "ASCII")
  750.         {
  751.         $encoding=$gettext_code;
  752.         }
  753.         elsif ($gettext_code ne "ASCII")
  754.         {
  755.         # Only report once because the message is quite long
  756.         if (! $encoding_problem_is_reported)
  757.         {
  758.             print STDERR "WARNING: You should use the same file encoding for all your project files,\n".
  759.                  "         but $PROGRAM thinks that most of the source files are in\n".
  760.                  "         $encoding encoding, while \"$_\" is (likely) in\n".
  761.                         "         $gettext_code encoding. If you are sure that all translatable strings\n".
  762.                  "         are in same encoding (say UTF-8), please \e[1m*prepend*\e[0m the following\n".
  763.                  "         line to POTFILES.in:\n\n".
  764.                  "                 [encoding: UTF-8]\n\n".
  765.                  "         and make sure that configure.in/ac checks for $PACKAGE >= 0.27 .\n".
  766.                  "(such warning message will only be reported once.)\n";
  767.             $encoding_problem_is_reported = 1;
  768.         }
  769.         }
  770.     }
  771.     }
  772.  
  773.     close OUTFILE;
  774.     close INFILE;
  775.  
  776.     unlink "$MODULE.pot";
  777.     my @xgettext_argument=("$XGETTEXT",
  778.                "--add-comments",
  779.                "--directory\=.",
  780.                            "--default-domain\=$MODULE",
  781.                            "--flag\=g_strdup_printf:1:c-format",
  782.                            "--flag\=g_string_printf:2:c-format",
  783.                            "--flag\=g_string_append_printf:2:c-format",
  784.                            "--flag\=g_error_new:3:c-format",
  785.                            "--flag\=g_set_error:4:c-format",
  786.                            "--flag\=g_markup_printf_escaped:1:c-format",
  787.                            "--flag\=g_log:3:c-format",
  788.                            "--flag\=g_print:1:c-format",
  789.                            "--flag\=g_printerr:1:c-format",
  790.                            "--flag\=g_printf:1:c-format",
  791.                            "--flag\=g_fprintf:2:c-format",
  792.                            "--flag\=g_sprintf:2:c-format",
  793.                            "--flag\=g_snprintf:3:c-format",
  794.                            "--flag\=g_scanner_error:2:c-format",
  795.                            "--flag\=g_scanner_warn:2:c-format",
  796.                "--output\=$MODULE\.pot",
  797.                "--files-from\=\.\/POTFILES\.in\.temp");
  798.     my $XGETTEXT_KEYWORDS = &FindPOTKeywords;
  799.     push @xgettext_argument, $XGETTEXT_KEYWORDS;
  800.     my $MSGID_BUGS_ADDRESS = &FindMakevarsBugAddress;
  801.     push @xgettext_argument, "--msgid-bugs-address\=\"$MSGID_BUGS_ADDRESS\"" if $MSGID_BUGS_ADDRESS;
  802.     push @xgettext_argument, "--from-code\=$encoding" if ($gettext_support_nonascii);
  803.     push @xgettext_argument, $XGETTEXT_ARGS if $XGETTEXT_ARGS;
  804.     my $xgettext_command = join ' ', @xgettext_argument;
  805.  
  806.     # intercept xgettext error message
  807.     print "Running $xgettext_command\n" if $VERBOSE;
  808.     my $xgettext_error_msg = `$xgettext_command 2>\&1`;
  809.     my $command_failed = $?;
  810.  
  811.     unlink "POTFILES.in.temp";
  812.  
  813.     print "Removing generated header (.h) files..." if $VERBOSE;
  814.     unlink foreach (@temp_headers);
  815.     print "done.\n" if $VERBOSE;
  816.  
  817.     if (! $command_failed)
  818.     {
  819.     if (! -e "$MODULE.pot")
  820.     {
  821.         print "None of the files in POTFILES.in contain strings marked for translation.\n" if $VERBOSE;
  822.     }
  823.     else
  824.     {
  825.         print "Wrote $MODULE.pot\n" if $VERBOSE;
  826.     }
  827.     }
  828.     else
  829.     {
  830.     if ($xgettext_error_msg =~ /--from-code/)
  831.     {
  832.             my $errlocation = "unknown";
  833.  
  834.             if ($xgettext_error_msg =~ /Non-ASCII string at (.*)\..*/)
  835.             {
  836.                 $errlocation = $1;
  837.             }
  838.             print STDERR "ERROR: xgettext failed to generate PO tempalte file because the following     \n".
  839.                          "       file contains strings marked for translation, not encoded in UTF-8.    \n".
  840.                          "       Please ensure all strings marked for translation are UTF-8 encoded.  \n\n".
  841.                          "           $errlocation\n\n";
  842.     }
  843.     else
  844.     {
  845.         print STDERR "$xgettext_error_msg";
  846.         if (-e "$MODULE.pot")
  847.         {
  848.         # is this possible?
  849.         print STDERR "ERROR: xgettext failed but still managed to generate PO template file.\n".
  850.                  "       Please consult error message above if there is any.\n";
  851.         }
  852.         else
  853.         {
  854.         print STDERR "ERROR: xgettext failed to generate PO template file. Please consult\n".
  855.                  "       error message above if there is any.\n";
  856.         }
  857.     }
  858.     exit (1);
  859.     }
  860. }
  861.  
  862. sub POFile_Update
  863. {
  864.     -f "$MODULE.pot" or die "$PROGRAM: $MODULE.pot does not exist.\n";
  865.  
  866.     my $MSGMERGE = $ENV{"MSGMERGE"} || "msgmerge";
  867.     my ($lang, $outfile) = @_;
  868.  
  869.     if (! isGNUGettextTool ("$MSGMERGE"))
  870.     {
  871.     print STDERR " *** GNU msgmerge is not found on this system!\n".
  872.              " *** Without it, intltool-update can not extract strings.\n";
  873.     exit;
  874.     }
  875.  
  876.     print "Merging $SRCDIR/$lang.po with $MODULE.pot..." if $VERBOSE;
  877.  
  878.     my $infile = "$SRCDIR/$lang.po";
  879.     $outfile = "$SRCDIR/$lang.po" if ($outfile eq "");
  880.  
  881.     # I think msgmerge won't overwrite old file if merge is not successful
  882.     system ("$MSGMERGE", "-o", $outfile, $infile, "$MODULE.pot");
  883. }
  884.  
  885. sub Console_WriteError_NotExisting
  886. {
  887.     my ($file) = @_;
  888.  
  889.     ## Report error if supplied language file is non-existing
  890.     print STDERR "$PROGRAM: $file does not exist!\n";
  891.     print STDERR "Try '$PROGRAM --help' for more information.\n";
  892.     exit;
  893. }
  894.  
  895. sub GatherPOFiles
  896. {
  897.     my @po_files = glob ("./*.po");
  898.  
  899.     @languages = map (&POFile_GetLanguage, @po_files);
  900.  
  901.     foreach my $lang (@languages) 
  902.     {
  903.     $po_files_by_lang{$lang} = shift (@po_files);
  904.     }
  905. }
  906.  
  907. sub POFile_GetLanguage ($)
  908. {
  909.     s/^(.*\/)?(.+)\.po$/$2/;
  910.     return $_;
  911. }
  912.  
  913. sub Console_Write_TranslationStatus
  914. {
  915.     my ($lang, $output_file) = @_;
  916.     my $MSGFMT = $ENV{"MSGFMT"} || "msgfmt";
  917.  
  918.     if (! isGNUGettextTool ("$MSGFMT"))
  919.     {
  920.     print STDERR " *** GNU msgfmt is not found on this system!\n".
  921.              " *** Without it, intltool-update can not extract strings.\n";
  922.     exit;
  923.     }
  924.  
  925.     $output_file = "$SRCDIR/$lang.po" if ($output_file eq "");
  926.  
  927.     system ("$MSGFMT", "-o", "$devnull", "--verbose", $output_file);
  928. }
  929.  
  930. sub Console_Write_CoverageReport
  931. {
  932.     my $MSGFMT = $ENV{"MSGFMT"} || "msgfmt";
  933.  
  934.     if (! isGNUGettextTool ("$MSGFMT"))
  935.     {
  936.     print STDERR " *** GNU msgfmt is not found on this system!\n".
  937.              " *** Without it, intltool-update can not extract strings.\n";
  938.     exit;
  939.     }
  940.  
  941.     &GatherPOFiles;
  942.  
  943.     foreach my $lang (@languages) 
  944.     {
  945.     print STDERR "$lang: ";
  946.     &POFile_Update ($lang, "");
  947.     }
  948.  
  949.     print STDERR "\n\n * Current translation support in $MODULE \n\n";
  950.  
  951.     foreach my $lang (@languages)
  952.     {
  953.     print STDERR "$lang: ";
  954.     system ("$MSGFMT", "-o", "$devnull", "--verbose", "$SRCDIR/$lang.po");
  955.     }
  956. }
  957.  
  958. sub SubstituteVariable
  959. {
  960.     my ($str) = @_;
  961.     
  962.     # always need to rewind file whenever it has been accessed
  963.     seek (CONF, 0, 0);
  964.  
  965.     # cache each variable. varhash is global to we can add
  966.     # variables elsewhere.
  967.     while (<CONF>)
  968.     {
  969.     if (/^(\w+)=(.*)$/)
  970.     {
  971.         ($varhash{$1} = $2) =~  s/^["'](.*)["']$/$1/;
  972.     }
  973.     }
  974.     
  975.     if ($str =~ /^(.*)\${?([A-Z_]+)}?(.*)$/)
  976.     {
  977.     my $rest = $3;
  978.     my $untouched = $1;
  979.     my $sub = "";
  980.         # Ignore recursive definitions of variables
  981.         $sub = $varhash{$2} if defined $varhash{$2} and $varhash{$2} !~ /\${?$2}?/;
  982.  
  983.     return SubstituteVariable ("$untouched$sub$rest");
  984.     }
  985.     
  986.     # We're using Perl backticks ` and "echo -n" here in order to 
  987.     # expand any shell escapes (such as backticks themselves) in every variable
  988.     return echo_n ($str);
  989. }
  990.  
  991. sub CONF_Handle_Open
  992. {
  993.     my $base_dirname = getcwd();
  994.     $base_dirname =~ s@.*/@@;
  995.  
  996.     my ($conf_in, $src_dir);
  997.  
  998.     if ($base_dirname =~ /^po(-.+)?$/) 
  999.     {
  1000.     if (-f "Makevars") 
  1001.     {
  1002.         my $makefile_source;
  1003.  
  1004.         local (*IN);
  1005.         open (IN, "<Makevars") || die "can't open Makevars: $!";
  1006.  
  1007.         while (<IN>) 
  1008.         {
  1009.         if (/^top_builddir[ \t]*=/) 
  1010.         {
  1011.             $src_dir = $_;
  1012.             $src_dir =~ s/^top_builddir[ \t]*=[ \t]*([^ \t\n\r]*)/$1/;
  1013.  
  1014.             chomp $src_dir;
  1015.                     if (-f "$src_dir" . "/configure.ac") {
  1016.                         $conf_in = "$src_dir" . "/configure.ac" . "\n";
  1017.                     } else {
  1018.                         $conf_in = "$src_dir" . "/configure.in" . "\n";
  1019.                     }
  1020.             last;
  1021.         }
  1022.         }
  1023.         close IN;
  1024.  
  1025.         $conf_in || die "Cannot find top_builddir in Makevars.";
  1026.     }
  1027.     elsif (-f "$SRCDIR/../configure.ac") 
  1028.     {
  1029.         $conf_in = "$SRCDIR/../configure.ac";
  1030.     } 
  1031.     elsif (-f "$SRCDIR/../configure.in") 
  1032.     {
  1033.         $conf_in = "$SRCDIR/../configure.in";
  1034.     } 
  1035.     else 
  1036.     {
  1037.         my $makefile_source;
  1038.  
  1039.         local (*IN);
  1040.         open (IN, "<Makefile") || return;
  1041.  
  1042.         while (<IN>) 
  1043.         {
  1044.         if (/^top_srcdir[ \t]*=/) 
  1045.         {
  1046.             $src_dir = $_;            
  1047.             $src_dir =~ s/^top_srcdir[ \t]*=[ \t]*([^ \t\n\r]*)/$1/;
  1048.  
  1049.             chomp $src_dir;
  1050.             $conf_in = "$src_dir" . "/configure.in" . "\n";
  1051.  
  1052.             last;
  1053.         }
  1054.         }
  1055.         close IN;
  1056.  
  1057.         $conf_in || die "Cannot find top_srcdir in Makefile.";
  1058.     }
  1059.  
  1060.     open (CONF, "<$conf_in");
  1061.     }
  1062.     else
  1063.     {
  1064.     print STDERR "$PROGRAM: Unable to proceed.\n" .
  1065.              "Make sure to run this script inside the po directory.\n";
  1066.     exit;
  1067.     }
  1068. }
  1069.  
  1070. sub FindPackageName
  1071. {
  1072.     my $version;
  1073.     my $domain = &FindMakevarsDomain;
  1074.     my $name = $domain || "untitled";
  1075.  
  1076.     &CONF_Handle_Open;
  1077.  
  1078.     my $conf_source; {
  1079.     local (*IN);
  1080.     open (IN, "<&CONF") || return $name;
  1081.     seek (IN, 0, 0);
  1082.     local $/; # slurp mode
  1083.     $conf_source = <IN>;
  1084.     close IN;
  1085.     }
  1086.  
  1087.     # priority for getting package name:
  1088.     # 1. GETTEXT_PACKAGE
  1089.     # 2. first argument of AC_INIT (with >= 2 arguments)
  1090.     # 3. first argument of AM_INIT_AUTOMAKE (with >= 2 argument)
  1091.  
  1092.     # /^AM_INIT_AUTOMAKE\([\s\[]*([^,\)\s\]]+)/m 
  1093.     # the \s makes this not work, why?
  1094.     if ($conf_source =~ /^AM_INIT_AUTOMAKE\(([^,\)]+),([^,\)]+)/m)
  1095.     {
  1096.     ($name, $version) = ($1, $2);
  1097.     $name    =~ s/[\[\]\s]//g;
  1098.     $version =~ s/[\[\]\s]//g;
  1099.     $name    =~ s/\(+$//g;
  1100.     $version =~ s/\(+$//g;
  1101.  
  1102.     $varhash{"PACKAGE_NAME"} = $name if (not $name =~ /\${?AC_PACKAGE_NAME}?/);
  1103.     $varhash{"PACKAGE"} = $name if (not $name =~ /\${?PACKAGE}?/);
  1104.     $varhash{"PACKAGE_VERSION"} = $version if (not $name =~ /\${?AC_PACKAGE_VERSION}?/);
  1105.     $varhash{"VERSION"} = $version if (not $name =~ /\${?VERSION}?/);
  1106.     }
  1107.     
  1108.     if ($conf_source =~ /^AC_INIT\(([^,\)]+),([^,\)]+)/m) 
  1109.     {
  1110.     ($name, $version) = ($1, $2);
  1111.     $name    =~ s/[\[\]\s]//g;
  1112.     $version =~ s/[\[\]\s]//g;
  1113.     $name    =~ s/\(+$//g;
  1114.     $version =~ s/\(+$//g;
  1115.  
  1116.     $varhash{"PACKAGE_NAME"} = $name if (not $name =~ /\${?AC_PACKAGE_NAME}?/);
  1117.     $varhash{"PACKAGE"} = $name if (not $name =~ /\${?PACKAGE}?/);
  1118.     $varhash{"PACKAGE_VERSION"} = $version if (not $name =~ /\${?AC_PACKAGE_VERSION}?/);
  1119.     $varhash{"VERSION"} = $version if (not $name =~ /\${?VERSION}?/);
  1120.     }
  1121.  
  1122.     # \s makes this not work, why?
  1123.     $name = $1 if $conf_source =~ /^GETTEXT_PACKAGE=\[?([^\n\]]+)/m;
  1124.     
  1125.     # m4 macros AC_PACKAGE_NAME, AC_PACKAGE_VERSION etc. have same value
  1126.     # as corresponding $PACKAGE_NAME, $PACKAGE_VERSION etc. shell variables.
  1127.     $name =~ s/\bAC_PACKAGE_/\$PACKAGE_/g;
  1128.  
  1129.     $name = $domain if $domain;
  1130.  
  1131.     $name = SubstituteVariable ($name);
  1132.     $name =~ s/^["'](.*)["']$/$1/;
  1133.  
  1134.     return $name if $name;
  1135. }
  1136.  
  1137.  
  1138. sub FindPOTKeywords
  1139. {
  1140.  
  1141.     my $keywords = "--keyword=_ --keyword=N_ --keyword=C_:1c,2 --keyword=NC_:1c,2 --keyword=Q_";
  1142.     my $varname = "XGETTEXT_OPTIONS";
  1143.     my $make_source; {
  1144.     local (*IN);
  1145.     open (IN, "<Makevars") || (open(IN, "<Makefile.in.in") && ($varname = "XGETTEXT_KEYWORDS")) || return $keywords;
  1146.     seek (IN, 0, 0);
  1147.     local $/; # slurp mode
  1148.     $make_source = <IN>;
  1149.     close IN;
  1150.     }
  1151.  
  1152.     # unwrap lines split with a trailing \
  1153.     $make_source =~  s/\\ $ \n/ /mxg;
  1154.     $keywords = $1 if $make_source =~ /^$varname[ ]*=\[?([^\n\]]+)/m;
  1155.     
  1156.     return $keywords;
  1157. }
  1158.  
  1159. sub FindMakevarsDomain
  1160. {
  1161.  
  1162.     my $domain = "";
  1163.     my $makevars_source; { 
  1164.     local (*IN);
  1165.     open (IN, "<Makevars") || return $domain;
  1166.     seek (IN, 0, 0);
  1167.     local $/; # slurp mode
  1168.     $makevars_source = <IN>;
  1169.     close IN;
  1170.     }
  1171.  
  1172.     $domain = $1 if $makevars_source =~ /^DOMAIN[ ]*=\[?([^\n\]\$]+)/m;
  1173.     $domain =~ s/^\s+//;
  1174.     $domain =~ s/\s+$//;
  1175.     
  1176.     return $domain;
  1177. }
  1178.  
  1179. sub FindMakevarsBugAddress
  1180. {
  1181.  
  1182.     my $address = "";
  1183.     my $makevars_source; { 
  1184.     local (*IN);
  1185.     open (IN, "<Makevars") || return undef;
  1186.     seek (IN, 0, 0);
  1187.     local $/; # slurp mode
  1188.     $makevars_source = <IN>;
  1189.     close IN;
  1190.     }
  1191.  
  1192.     $address = $1 if $makevars_source =~ /^MSGID_BUGS_ADDRESS[ ]*=\[?([^\n\]\$]+)/m;
  1193.     $address =~ s/^\s+//;
  1194.     $address =~ s/\s+$//;
  1195.     
  1196.     return $address;
  1197. }
  1198.